有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

用线程分配java内存

我想知道如果在一个方法中声明一个本地线程会发生什么?通常,函数返回时,所有局部变量都将消失,因为它们都是在堆栈上分配的。然而,本地线程似乎是另一回事。是这样吗

public int A() {
    Thread t = new Thread() {
        doSomething();
    }
    t.start();
    return -1;
 }

共 (5) 个答案

  1. # 1 楼答案

    如果变量是一个原语,那么它将在堆栈上,并且在方法返回时将消失——但是线程的Runnable实例(或任何包含线程内容的实例)将具有该原语值的副本

    如果变量是引用类型,那么对象将被分配到堆上并一直存在,直到不再有对它的引用为止,此时它可以进行垃圾收集。对该对象的引用位于堆栈上,并且在方法返回时将消失,但与原语一样,线程的Runnable将具有该引用的副本(从而使该对象保持活动状态)

  2. # 2 楼答案

    如果线程是从本地上下文启动的,则该线程将继续执行,直到其可运行的run方法完成执行为止

  3. # 3 楼答案

    约翰的回答很好,但我想我应该补充一些细节。下面是一个代码示例,我将使用它来显示特定的变量用法

     public void startThread() {
          long var1 = 10;
          byte[] var2 = new byte[1024];
          final byte[] var3 = new byte[1024];
          final byte[] var4 = new byte[1024];
          Thread thread = new Thread(new Runnable() {
              private long var5 = 10;
              private byte[] var6 = new byte[1024];
              public void run() {
                  int var7 = 100;
                  byte[] var8 = new byte[1024];
                  System.out.println("Size of var4 is " + var4.length);
                  baz();
                  ...
              }
              private void baz() {
                  long var9 = 2;
                  byte[] var10 = new byte[1024];
                  ...
              }
          });
          thread.start();
     }
    

    因此,我们在线程周围分配了许多变量。我们还有Thread对象本身以及线程正在运行的Runnable目标

    • 线程——尽管它看起来是startThread()的本地线程,但关联的Thread也由JVM管理。只有在run()方法完成并且Thread被JVM获取之后,它才会被GC'd。在Thread被GC'd之后,那么Thread使用的所有字段都可以被GC'd
    • Runnable——这个匿名类是线程正在运行的。它可以在Thread完成后进行GC'd,并且是GC'd
    • var1——这是startThread()的本地值,并在堆栈上分配。当startThread()方法完成并且堆栈被重用时,它将被覆盖
    • var2——这是startThread()的本地代码,并在堆上分配。线程无法使用它,因为它不是final。它可以在startThread()完成后进行GC
    • var3——这是startThread()的本地代码,并在堆上分配。这是final,因此线程可以使用它,但它不是。它可以在startThread()完成后进行GC
    • var4——这是startThread()的本地代码,并在堆上分配。这是final,线程使用它。只有在startThread()方法完成RunnableThread都被GC'd,它才能被GC'd
    • var5——这是Runnable内部的一个本地字段,在堆上作为Runnable匿名类的一部分进行分配。它可以在Runnable完成并且RunnableThread被GC'd之后被GC'd
    • var6——这是Runnable内部的一个本地字段,在堆上分配。它可以在Runnable完成并且RunnableThread被GC'd之后被GC'd
    • var7——这是run()方法内部的一个局部字段,分配给新线程的堆栈。当run()方法完成并且堆栈被重用时,它将被覆盖
    • var8——这是run()方法内部的一个本地字段,在堆上分配。它可以在run()方法完成后进行GC
    • var9——这是baz()方法内部的一个局部字段,分配给新线程的堆栈。当baz()方法完成并且堆栈被重用时,它将被覆盖
    • var10——这是baz()方法内部的一个本地字段,在堆上分配。它可以在baz()方法完成后进行GC

    其他几点注意事项:

    • 如果新线程从未启动,那么一旦startThread()完成,就可以对其进行GC。Runnable以及与之相关的所有变量也可以进行GC
    • 如果在startThread()中声明了一个final long varX原语并在线程中使用,那么它必须在堆上分配,而不是在堆栈上分配。当startThread()完成时,它仍将被使用
  4. # 4 楼答案

    如果在方法中生成局部Thread,则只有声明为final的局部方法变量将一直保留到Thread完成为止。当Thread完成它的run()方法时,线程和它从创建它的方法中获得的任何最终变量都将像其他任何东西一样被垃圾收集

    澄清

    只有原始方法和派生线程的run()方法中使用的final变量在方法和run()方法完成之前不会被垃圾收集。如果线程不访问变量,那么线程的存在不会阻止在原始方法完成后对变量进行垃圾收集

    参考资料

    http://java.sun.com/docs/books/performance/1st_edition/html/JPAppGC.fm.html

  5. # 5 楼答案

    线程是它自己的GC根。因此,无论何时创建线程(不管其创建上下文如何),在其run方法完成之前,它都不会准备好进行GC。即使本地方法完成并且线程仍然处于活动状态,这也是正确的

    例如:

    public void doSomeAsync(){
       Thread th = new Thread(new Runnable(){
          public void run(){
              Thread.sleep(500);
          }
       });
       th.start();
       //do something else quickly
    }
    

    //do somethign else quickly之后,任何未逃逸该方法的定义都将标记为GC。线程th将不会被标记为GC,而是正确地放置在具有自己线程堆栈的堆上